package com.bella.api.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.*;

/**
 *
 * @author 李杰 E-mail:605779485@qq.com
 * @version 1.0.0 创建时间：2019年4月1日
 *
 */
public class RedisClient {
    private Logger log = LoggerFactory.getLogger(RedisClient.class);

    private JedisPool jedisPool;

    private static String SERIAL_NUMBER = ".serial.number:";

    public void set(String key, Object value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value.toString());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            jedis.close();
        }
    }

    /***
     * 分布式获取锁
     * @param key 锁
     * @param exptime 过期时间
     * @param isSpan  是否自旋
     * **/
    public boolean lock(String key, int exptime,boolean isSpan) {
        Jedis jedis = null;
        try {
        	int cnt = 5;    //自旋次数
        	while(cnt>0) {
        		jedis = jedisPool.getResource();
        		if(jedis.setnx(key, Thread.currentThread().getId()+"")==1) {
        			jedis.expire(key, exptime);
        			return true;
        		}else {
        			if(isSpan) {
        				cnt--;
        				Thread.sleep(1000);
        				continue;
        			}
        			return false;
        		}
        	}
        	return false;
        }
        catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        finally {
            jedis.close();
        }
    }

    /***
     * 分布式释放锁
     * @param key 锁
     * **/
    public void releaseLock(String key) {
        Jedis jedis = null;
        try {
        	jedis = jedisPool.getResource();
        	String value = jedis.get(key);
        	if(value!=null && value.equalsIgnoreCase(Thread.currentThread().getId()+"")) {
        		jedis.del(key);
        	}
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            jedis.close();
        }
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param value
     * @param exptime
     * @throws Exception
     */
    public boolean setWithExpireTime(String key, String value, int exptime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return "OK".equalsIgnoreCase(jedis.set(key, value, "NX", "EX", exptime));
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        } finally {
            jedis.close();
        }
        return false;
    }

    public String get(String key) {

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.get(key);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (jedis != null)
                jedis.close();
        }
        return null;
    }

    /**
     * 存储任意类型为Json格式数据
     *
     * @param key
     *            存储键
     * @param value
     *            存储值
     * @param timeOut
     *            是否设置过期失效
     * @param seconds
     *            有效时间(单位：秒)
     * @return
     * @throws Exception
     */
    public boolean set(String key, Object value, boolean timeOut, Integer seconds) throws Exception {

        if (StringUtil.isBlankOr(key, value)) {
            log.error("Redis set key or value 为空");
            return false;
        }

        Jedis jedis = null;
        try {
            delObj(key);
            jedis = jedisPool.getResource();
            boolean result = jedis.set(key, JSON.toJSONString(value)).toUpperCase().equalsIgnoreCase("OK");
            if (timeOut && seconds != null) {
                jedis.expire(key, seconds);
            }
            return result;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 存储Map
     *
     * @param key
     *            键
     * @param map
     *
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public boolean set(String key, Map map) throws Exception {

        if (StringUtil.isBlankOr(key, map)) {
            log.error("Redis 存储Map key or value 为空");
            return false;
        }

        Jedis jedis = null;
        try {
            delObj(key);
            jedis = jedisPool.getResource();
            boolean result = jedis.hmset(key, map).equalsIgnoreCase("OK");
            return result;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 存储List
     *
     * @param <W>
     * @param key
     * @param list
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public <W extends List> boolean set(String key, W list) throws Exception {

        if (StringUtil.isBlankOr(key, list)) {
            log.error("Redis 存储List key or value 为空");
            return false;
        }

        Jedis jedis = null;
        try {
            delObj(key); // 先删除可能存在的KEY
            jedis = jedisPool.getResource();
            for (Object obj : list) {
                jedis.lpush(key, JSON.toJSONString(obj));
            }
            return true;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 存储Set
     *
     * @param key
     * @param set
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public boolean set(String key, Set set) throws Exception {

        if (StringUtil.isBlankOr(key, set)) {
            log.error("Redis 存储Set key or value 为空");
            return false;
        }

        Jedis jedis = null;
        try {
            delObj(key);
            jedis = jedisPool.getResource();
            for (Object obj : set) {
                jedis.sadd(key, JSON.toJSONString(obj));
            }
            return true;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 获取Map值
     *
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public Map<String, Object> getMap(String key) throws Exception {
        Jedis jedis = null;
        try {
            if (key == null || key.equals("")) {
                return null;
            }
            jedis = jedisPool.getResource();
            Set<String> keySet = jedis.hkeys(key);
            if (keySet == null || keySet.isEmpty()) {
                return null;
            }
            Map<String, Object> data = new HashMap<String, Object>();
            String tempKey = null;
            List tempList = null;
            for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext();) {
                tempKey = iterator.next();
                tempList = jedis.hmget(key, tempKey);
                data.put(tempKey, tempList != null ? tempList.get(0) : null);
            }
            return data;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * 获取List类型数据
     *
     * @param key
     * @param cls
     *            List集合中保存的对象
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public List getList(String key, Class cls) {
        Jedis jedis = null;
        try {
            if (key == null || key.equals("")) {
                return null;
            }
            if (cls == null) {
                cls = Object.class;
            }
            jedis = jedisPool.getResource();
            List<String> list = jedis.lrange(key, 0, -1);
            if (list == null || list.isEmpty()) {
                return null;
            }
            List<Object> data = new ArrayList<Object>();
            for (String str : list) {
                data.add(JSONObject.parseObject(str, cls));
            }
            return data;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * 获取Set类型数据
     *
     * @param key
     * @param cls
     *            Set集合中保存的对象
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Set getSet(String key, Class cls) throws Exception {
        Jedis jedis = null;
        try {
            if (key == null || key.equals("")) {
                return null;
            }
            if (cls == null) {
                cls = Object.class;
            }
            jedis = jedisPool.getResource();
            Set<String> set = jedis.smembers(key);
            if (set == null || set.isEmpty()) {
                return null;
            }
            Set<Object> data = new HashSet<Object>();
            for (String str : set) {
                data.add(JSONObject.parseObject(str, cls));
            }
            return data;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * 删除指定Key
     *
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public boolean delObj(String key) throws Exception {
        Jedis jedis = null;
        try {
            if (key == null || key.equals("")) {
                return false;
            }
            jedis = jedisPool.getResource();
            boolean exists = jedis.exists(key);
            if (!exists)
                return true;
            return jedis.del(key) > 0 ? true : false;
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 删除Hash指定KEY
     *
     * @param mainKey
     *            Hash主键
     * @param key
     *            Hash子键
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public boolean hdelObj(String mainKey, String key) throws Exception {

        if (StringUtil.isBlankOr(mainKey, key))
            return false;

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            boolean exists = hExists(mainKey, key);
            if (!exists)
                return true;
            return jedis.hdel(mainKey, key) > 0 ? true : false;
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 判断Key是否存在
     *
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public boolean exists(String key) throws Exception {

        if (StringUtil.isBlank(key))
            return false;

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.exists(key);
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 判断Hash中Key是否存在
     *
     * @param mainKey
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public boolean hExists(String mainKey, String key) throws Exception {

        if (StringUtil.isBlankOr(mainKey, key))
            return false;

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.hexists(mainKey, key);
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;

    }

    /**
     * 设置有效时间
     *
     * @param key
     * @param seconds
     *            有效时间(单位：秒)
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public boolean expire(String key, int seconds) throws Exception {

        if (StringUtil.isBlank(key))
            return false;

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.expire(key, seconds) > 0 ? true : false;
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 获取还剩多长时间过期
     *
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public long getTTL(String key) throws Exception {

        if (StringUtil.isBlank(key))
            return 0L;

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis.exists(key)) {
                return jedis.ttl(key);
            }
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return -2;
    }

    /**
     * 获取Value类型
     *
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public String getValueType(String key) throws Exception {

        if (StringUtil.isBlank(key))
            return "";

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.type(key);
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * 获取当前Redis中所有KEY
     *
     * @param key
     * @return
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public Set<String> getAllKeys(String key) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Set<String> set = jedis.keys(key);
            return set;
        }
        catch (Exception e) {
            jedisPool.returnBrokenResource(jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * 一旦拿到的jedis实例使用完毕，必须要返还给池中
     *
     * @param pool
     * @param jedis
     */
    @SuppressWarnings("deprecation")
    public void returnResource(JedisPool pool, Jedis jedis) {
        try {
            // 这里很重要，一旦拿到的jedis实例使用完毕，必须要返还给池中
            if (pool != null && jedis != null) {
                pool.returnResource(jedis);
            }
        }
        catch (Exception e) {
            log.error("returnResource 异常：" + e.getMessage(), e);
        }
    }

    /**
     * 释放redis对象
     *
     * @param pool
     * @param jedis
     */
    @SuppressWarnings("deprecation")
    public void returnBrokenResource(JedisPool pool, Jedis jedis) {
        try {
            if (pool != null && jedis != null) {
                pool.returnBrokenResource(jedis);
            }
        }
        catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * Redis_Hash存储
     *
     * @param mainKey
     *            主键
     * @param key
     *            子键
     * @param obj
     *            存储值
     * @return
     * @throws Exception
     */

    public boolean hset(String mainKey, String key, Object obj) throws Exception {
        return hset(mainKey, key, obj, false, null);
    }

    /**
     * Redis_Hash存储，设计为私有方法，目前hset设置失效时间无用
     *
     * @param mainKey
     * @param key
     * @param obj
     * @param isTimeOut
     * @param seconds
     * @return
     * @throws Exception
     */
    public boolean hset(String mainKey, String key, Object obj, boolean isTimeOut, Integer seconds) throws Exception {

        if (StringUtil.isBlankOr(mainKey, key, obj)) {
            return false;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.hset(mainKey, key, JSON.toJSONString(obj));
            if (isTimeOut == true && null != seconds) {
                jedis.expire(key, seconds);
            }
            return true;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * Redis_Hash存储，设计为私有方法，目前hset设置失效时间无用
     *
     * @param mainKey
     * @param key
     * @param obj
     * @param isTimeOut
     * @param seconds
     * @return
     * @throws Exception
     */
    public long hsetnx(String key, String field, String value, boolean isTimeOut, Integer seconds) throws Exception {

        if (StringUtil.isBlankOr(key, field, value)) {
            return 0L;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            long ret = jedis.hsetnx(key, field, value);
            if (isTimeOut == true && null != seconds) {
                jedis.expire(key, seconds);
            }
            return ret;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return 0L;
    }

    /**
     * 获取任意对象类型数据，将Json格式字符串转换成对象
     *
     * @param <W>
     * @param key
     * @param t
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public <W extends Object> W getObject(String key, Class t) throws Exception {
        String data = getObject(key);
        if (null == data || data.equals("") || null == t)
            return null;
        return (W) JSON.parseObject(data, t);
    }

    /**
     * 获取Hash值
     *
     * @param <T>
     * @param mainKey
     * @param key
     * @param t
     * @return
     * @throws Exception
     */
    public <T> T getObject(String mainKey, String key, Class<T> t) throws Exception {
        String data = getObject(mainKey, key);
        if (null == data || data.equals("") || null == t)
            return null;
        return JSON.parseObject(data, t);
    }

    public String getObject(String mainKey, String key) throws Exception {

        if (key == null || key.equals("")) {
            return null;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String data = jedis.hget(mainKey, key);
            if (data == null || data.equals("")) {
                return null;
            }
            return data;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    public String getObject(String key) throws Exception {

        if (key == null || key.equals("")) {
            return null;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String data = jedis.get(key);
            if (data == null || data.equals("")) {
                return null;
            }
            return data;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * 查询Hash Key下的所有hash值
     *
     * @param key
     * @return
     * @throws Exception
     */
    public Map<String, String> hgetAll(String key) throws Exception {

        if (StringUtil.isBlank(key)) {
            return null;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.hgetAll(key);
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * 查询Hash Key下的field值
     *
     * @param key
     * @return
     * @throws Exception
     */
    public String hget(String key, String field) throws Exception {

        if (StringUtil.isBlank(key)) {
            return null;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.hget(key, field);
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    /**
     * Redis_set存储
     *
     * @return
     * @throws Exception
     */
    public boolean sadd(String key, String member, boolean isTimeOut, Integer seconds) throws Exception {

        if (StringUtil.isBlankOr(key, member)) {
            return false;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.sadd(key, member);
            if (isTimeOut == true && null != seconds) {
                jedis.expire(key, seconds);
            }
            return true;
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return false;
    }

    /**
     * 判断 member 是否是key集合的元素
     *
     * @param key
     * @return
     * @throws Exception
     */
    public Boolean sismember(String key, String member) throws Exception {

        if (StringUtil.isBlank(key)) {
            return null;
        }

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.sismember(key, member);
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return null;
    }

    public Long incr(String key) throws Exception {
        Jedis jedis = null;
        try {
            if (key == null || key.equals("")) {
                return 0L;
            }
            jedis = jedisPool.getResource();
            return jedis.incr(key);
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return 0L;
    }

    public Long decr(String key) throws Exception {
        Jedis jedis = null;
        try {
            if (key == null || key.equals("")) {
                return 0L;
            }
            jedis = jedisPool.getResource();
            return jedis.decr(key);
        }
        catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        }
        finally {
            returnResource(jedisPool, jedis);
        }
        return 0L;
    }


    /***
     * 自加指定的数量
     */
    public Long incrBy(String key,Long by) {
        Jedis jedis = null;
        try {
            if (key == null || key.equals("")) {
                return 0L;
            }
            jedis = jedisPool.getResource();
            jedis.setnx(key,"0");
            return jedis.incrBy(key,by);
        } catch (Exception e) {
            returnBrokenResource(jedisPool, jedis);
            log.error(e.getMessage(), e);
        } finally {
            returnResource(jedisPool, jedis);
        }
        return 0L;
    }

    //外卖78+区域01+190705+序列00000001
    public String makeSeqNo(String business,String region,String tranTime,int length) throws Exception {
        String today = tranTime.substring(2,8);
        /** 构造redis的key */
        String key = business + SERIAL_NUMBER + today;
        /** 自增 */
        Long sequence = null;
        if(this.exists(key)){
             sequence = this.incr(key);
        }else{
             sequence = this.incr(key);
            this.expire(key,100000);
        }

        String seq = this.getSequence(sequence,length);
        StringBuilder builder = new StringBuilder(business);
        builder.append(region).append(today).append(seq);
        return builder.toString();
    }


    /**
     * 得到6位的序列号,长度不足6位,前面补0
     *
     * @param seq
     * @return
     */
    private String getSequence(long seq,int length) {
        String str = String.valueOf(seq);
        int len = str.length();
        if (len >= length) {// 取决于业务规模
            return str;
        }
        int rest = length - len;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < rest; i++) {
            sb.append('0');
        }
        sb.append(str);
        return sb.toString();
    }

    public JedisPool getJedisPool() {
        return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

}
